package com.xushuda.yas.storage;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.Writer;

/**
 * 用于记录对数据的操作，只有insert，update和delete会产生新的序列号，query不会产生新的序列号，
 * 
 * @author xushuda
 *
 */
public class OpLog {

    private String baseDir;

    private static String DIR_SEPERATOR = "/";

    /**
     * Data Management Operation 数据管理操作; update实际上是插入新的数据，并在原有数据上把结束版本号设置为当前操作的版本号
     * 
     * @author xushuda
     *
     */
    public enum DMOP {
        INSERTION(0), UPDATE(1), DELETE(2);

        public final int type;

        private DMOP(int type) {
            this.type = type;
        }

        public static DMOP getType(int type) {
            if (type == INSERTION.type) {
                return INSERTION;
            } else if (type == UPDATE.type) {
                return UPDATE;
            } else {
                return DELETE;
            }
        }
    }

    class LogLine {

        private static final String FIELD_DELIMITER = "\t";
        private static final String ROW_DELIMITER = "\n";

        // log 日志不需要修改，所以long的数据直接写入即可，不需要严格占用8个字节
        long seqNo;
        String key;
        String value;
        DMOP op;

        public LogLine(String raw) {
            String[] dt = raw.split(FIELD_DELIMITER);
            seqNo = Long.parseLong(dt[0]);
            key = dt[1];
            value = dt[2];
            op =
                    Integer.parseInt(dt[3].replace(ROW_DELIMITER, "")) == DMOP.INSERTION.type ? DMOP.INSERTION
                            : DMOP.UPDATE;
        }

        public LogLine(long seqNo, String key, String value, DMOP op) {
            this.seqNo = seqNo;
            this.key = key;
            this.value = value;
            this.op = op;
        }

        public String toString() {

            StringBuilder sb = new StringBuilder();

            sb.append(seqNo).append(FIELD_DELIMITER).append(key).append(FIELD_DELIMITER).append(value)
                    .append(FIELD_DELIMITER).append(op.type).append(ROW_DELIMITER);
            return sb.toString();
        }
    }

    // 当前最大序列号
    private long seqNo;
    private Writer writer;

    /**
     * 
     * @param path 日志文件位置
     * @throws IOException
     */
    public OpLog(String dir) throws IOException {

        this.baseDir = dir.endsWith(DIR_SEPERATOR) ? dir : dir + DIR_SEPERATOR;

        File dirf = new File(baseDir);
        if (!dirf.exists()) {
            dirf.mkdirs();
        }

        String path = baseDir + "log";

        // 记录log序列号的数据
        writer = new FileWriter(path, true);

        // BufferedReader br = new BufferedReader(new FileReader(path));

        // 初始化seqNo值
        RandomAccessFile rf = new RandomAccessFile(new File(path), "r");
        long len = rf.length();
        seqNo = 0L;
        if (len != 0L) {
            long pos = len - 1;
            while (pos > 0) {
                pos--;
                rf.seek(pos);
                if (rf.readByte() == '\n') {
                    String line = rf.readLine();
                    try {
                        // 一般是IndexOutOfRange ，因为写入不完全,那么跳到上一行
                        // TODO 删除这一行,或者修复这一行
                        LogLine ll = new LogLine(line);
                        seqNo = ll.seqNo; // 序列号
                    } catch (Exception e) {
                        continue;
                    }

                    break;
                }
            }

        }
        rf.close();
    }

    /**
     * 记录一条log 并返回这条log的序列号
     * 
     * @param k
     * @param v
     * @param op
     * @return 版本号
     * @throws IOException
     */
    public long writeLog(String key, String value, DMOP op) throws IOException {
        writer.append(new LogLine(++seqNo, key, value, op).toString());
        // 这里需要强稳定性应该flush,因为writer本身都有buffer,所以真正写入可能会延迟
        // writer.flush(); // FIXME
        return seqNo;
    }

    /**
     * 获取当前序列号
     * 
     * @return
     */
    public long getSeq() {
        return seqNo;
    }

}
